#pragma once
#include <string>
#include "Socket.hpp"
#include "Log.hpp"
#include <sys/epoll.h>

using namespace socket_ns;

class EpollServer
{
    const static int gnum = 64;

public:
    EpollServer(uint16_t port = 8888)
        : _port(port), _listenSock(std::make_unique<TcpSocket>()), _epfd(-1)
    {
        InetAddr clinetAddr("0", _port);
        _listenSock->BuildListenSocket(clinetAddr);
        _epfd = epoll_create(128);
        if (_epfd < 0)
        {
            LOG(FATAL, "epoll_create failed\n");
            exit(5);
        }
        LOG(DEBUG, "epoll_create success, epfd: %d\n", _epfd);
        // 创建epoll模型成功后，刚开始只有listen套接字，将listen套接字设置进红黑树
        struct epoll_event event;
        event.events = EPOLLIN;
        event.data.fd = _listenSock->SockFd();
        epoll_ctl(_epfd, EPOLL_CTL_ADD, _listenSock->SockFd(), &event);
    }
    ~EpollServer()
    {
        if (_epfd >= 0)
            ::close(_epfd);
        _listenSock->Close();
    }
    void HandlerEvent(int n)
    {
        for (int i = 0; i < n; i++)
        {
            uint32_t revents = _ev[i].events;
            int fd = _ev[i].data.fd;
            // 是什么事件就绪了呢
            if (revents & EPOLLIN)
            {
                // 是listen套接字读事件就绪了，还是normal fd呢
                if (fd == _listenSock->SockFd())
                {
                    InetAddr clientaddr;
                    int newfd = _listenSock->Accepter(&clientaddr);
                    if (newfd < 0)
                        continue;
                    struct epoll_event event;
                    event.events = EPOLLIN;
                    event.data.fd = newfd;
                    ::epoll_ctl(_epfd, EPOLL_CTL_ADD, newfd, &event);
                    LOG(DEBUG, "_listensock ready, accept done, epoll_ctl done, newfd is: %d\n", newfd);
                }
                else
                {
                    char buffer[1024];
                    ssize_t n = ::recv(fd, buffer, sizeof(buffer) - 1, 0);
                    if (n > 0)
                    {
                        LOG(DEBUG, "normal fd: %d recv begin...\n");
                        buffer[n] = 0;
                        std::cout << "client say# " << buffer << std::endl;
                        std::string echo_string = "Server echo# ";
                        echo_string += buffer;
                        ::send(fd, echo_string.c_str(), echo_string.size(), 0);
                    }
                    else if (n == 0)
                    {
                        LOG(DEBUG, "normal fd %d close, me too!\n", fd);
                        // 先删除红黑树节点，再关闭fd
                        ::epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr);
                        ::close(fd);
                    }
                    else
                    {
                        LOG(DEBUG, "normal fd %d close, me too!\n", fd);
                        // 先删除红黑树节点，再关闭fd
                        ::epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr);
                        ::close(fd);
                    }
                }
            }
        }
    }
    void Loop()
    {
        int timeout = -1; // 毫秒为单位
        while (true)
        {
            int n = epoll_wait(_epfd, _ev, gnum, timeout);
            switch (n)
            {
            case 0:
                LOG(INFO, "timeout...\n"); // 刚开始只有一个listen套接字，没人链接会一直超时
                break;
            case -1:
                LOG(ERROR, "epoll error\n");
                break;
            default:
            {
                LOG(DEBUG, "epoll_wait events have ready..n: %d\n", n); // 虽然刚开始只有一个listen套接字，一旦有人连接，就会一直就绪，我们需要处理事件
                HandlerEvent(n);
            }
            break;
            }
        }
    }

private:
    uint16_t _port;
    std::unique_ptr<Socket> _listenSock;
    struct epoll_event _ev[gnum];
    int _epfd;
};